/*
 * Copyright 2019-2025 the original author or authors.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * https://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.iiifi.kite.security.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.env.Environment;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.iiifi.kite.core.exception.ServiceException;
import com.iiifi.kite.core.model.KiteAccount;
import com.iiifi.kite.core.result.Result;
import com.iiifi.kite.core.spring.SpringContextUtils;
import com.iiifi.kite.configuration.KiteProperties;
import com.iiifi.kite.security.annotation.KitePermissions;
import com.iiifi.kite.security.enums.KiteSecurityCode;
import com.iiifi.kite.security.handle.KitePermissionsFun;
import com.iiifi.kite.security.model.KiteAuthForm;
import com.iiifi.kite.security.service.KiteSecurityService;
import com.iiifi.kite.security.utils.KiteUtils;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * Kite-Auth web 拦截器
 *
 * @author kite@iiifi.com 花朝
 */

@Slf4j
@AllArgsConstructor
public class KiteSecurityInterceptor extends HandlerInterceptorAdapter {

    private final Environment environment;

    private final KiteProperties kiteProperties;

    private final KiteSecurityService kiteSecurityService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 非控制器请求直接跳出
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        // 查询用户信息 判断登录是否有效
        KiteAccount kiteAccount = kiteSecurityService.auth(assembleAuthForm(request));
        // 将用户信息存放到 request 作用域中便于后续获取
        request.setAttribute(KiteUtils.ACCOUNT_ATTRIBUTES_ATTRIBUTE, kiteAccount);
        request.setAttribute(kiteProperties.getHeaders().getAccountId(), kiteAccount.getId());
        // 判断是否有具体的访问权限
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        KitePermissions permissions = KiteUtils.getAnnotation(handlerMethod, KitePermissions.class);
        if (permissions != null) {
            if (handlePermissions(permissions)) {
                return true;
            } else {
                throw new ServiceException(Result.fail(KiteSecurityCode.AUTH_IS_FORBIDDEN));
            }
        }
        return super.preHandle(request, response, handler);
    }

    /**
     * 判断是否有权限
     *
     * @param permissions KitePermissions注解
     */
    private boolean handlePermissions(KitePermissions permissions) {

        ExpressionParser elParser = new SpelExpressionParser();
        // 注解表达式
        String condition = permissions.value();
        if (StringUtils.hasText(condition)) {
            Expression expression = elParser.parseExpression(condition);
            StandardEvaluationContext context = getEvaluationContext();
            return expression.getValue(context, Boolean.class);
        }
        return false;
    }

    /**
     * 获取方法上的参数
     *
     * @return {SimpleEvaluationContext}
     */
    private StandardEvaluationContext getEvaluationContext() {

        // 初始化Sp el表达式上下文，并设置 SecurityFun
        StandardEvaluationContext context = new StandardEvaluationContext(new KitePermissionsFun());
        // 设置表达式支持spring bean
        assert SpringContextUtils.getContext() != null;
        context.setBeanResolver(new BeanFactoryResolver(SpringContextUtils.getContext()));
        return context;
    }

    /**
     * 封装鉴权对象
     *
     * @param request 请求
     * @return KiteAuthForm 鉴权对象
     */
    private KiteAuthForm assembleAuthForm(HttpServletRequest request) {
        // 获取token
        String token = KiteUtils.getToken(request, kiteProperties);
        String deviceId = request.getHeader(kiteProperties.getHeaders().getDeviceId());
        if (!StringUtils.hasText(token)) {
            throw new ServiceException(KiteSecurityCode.TOKEN_IS_INVALID);
        }
        // 获取url和method
        String requestUrl = request.getRequestURL().toString();
        String method = request.getMethod().toUpperCase();
        // 获取 appName
        String appName = environment.getProperty("spring.application.name");
        // 封装鉴权对象
        KiteAuthForm kiteAuthForm = new KiteAuthForm();
        kiteAuthForm.setAppName(appName);
        kiteAuthForm.setMethod(method);
        kiteAuthForm.setPath(requestUrl);
        kiteAuthForm.setParameters(KiteUtils.getRequestParamMap(request));
        kiteAuthForm.setToken(token);
        kiteAuthForm.setDeviceId(deviceId);
        return kiteAuthForm;
    }
}
